package com.lsh.oms.core.service.other;

import com.alibaba.fastjson.JSONObject;
import com.lsh.base.lang.Maps;
import com.lsh.oms.api.model.order.receipt.ReceiptHeadDto;
import com.lsh.oms.core.constant.ReturnCode;
import com.lsh.oms.core.dao.order.OrderHeadDao;
import com.lsh.oms.core.dao.other.OrderShippingDetailMapper;
import com.lsh.oms.core.dao.other.OrderShippingHeadMapper;
import com.lsh.oms.core.dao.other.OrderSignDetailMapper;
import com.lsh.oms.core.dao.other.OrderSignHeadMapper;
import com.lsh.oms.core.enums.*;
import com.lsh.oms.core.exception.BusinessException;
import com.lsh.oms.core.model.order.*;
import com.lsh.oms.core.model.po.OmsTask;
import com.lsh.oms.core.proxy.service.PaymentProxy;
import com.lsh.oms.core.records.core.ReturnRequestRepository;
import com.lsh.oms.core.service.order.OrderQueryService;
import com.lsh.oms.core.service.order.OrderUpdateService;
import com.lsh.oms.core.service.po.OmsTaskService;
import com.lsh.oms.core.service.shipping.core.ShippingService;
import com.lsh.oms.core.utils.DateUtil;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.lsh.base.lang.BigDecimals.greaterThanZero;
import static com.lsh.base.lang.BigDecimals.subtractWithScale4;
import static com.lsh.oms.core.records.core.ReturnRequest.Occasion.BEFORE_SIGN;

/**
 * Created by xuzhan on 16/11/11.
 */
@Service
@SuppressWarnings("all")
public class SignOrderService {

    private static Logger logger = LoggerFactory.getLogger(SignOrderService.class);

    @Autowired
    private OrderShippingDetailMapper detailDao;

    @Autowired
    private OrderHeadDao orderHeadDao;

    @Autowired
    private OrderShippingHeadMapper headDao;

    @Autowired
    private OrderShippingHeadMapper shippingDao;

    @Autowired
    private OrderSignHeadMapper orderSignHeadMapper;

    @Autowired
    private OrderSignDetailMapper orderSignDetailMapper;

    @Autowired
    private OrderUpdateService orderUpdateService;

    @Autowired
    private ReturnRequestRepository returnRequests;

    @Resource
    private ShippingService shippings;

    @Resource
    private PaymentProxy paymentProxy;

    @Resource
    private OrderQueryService orderQueryService;

    @Resource
    private OmsTaskService tasks;

    @Transactional
    public boolean saveOrUpdateData(OrderSignHead oderSignHead, String logSign, boolean statusFlag, boolean flagKA) {
        //插入签收单表
        orderSignHeadMapper.insertSelective(oderSignHead);
        for (OrderSignDetail detail : oderSignHead.getDetails()) {
            //签收单detail
            orderSignDetailMapper.insertSelective(detail);
        }
        int sysTime = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
        if (statusFlag || flagKA) {
            OrderShippingHead shippingHead = new OrderShippingHead();
            shippingHead.setStatus(ShippingStatus.DELIVERED.getIndex());
            shippingHead.setShippingOrderId(oderSignHead.getShippingOrderId());
            shippingHead.setArrivedAt(sysTime);
            shippingHead.setActivedAt(sysTime);
            shippingHead.setReceiptStatus(ShippingReceiptStatus.RECEIPT.getIndex());
            shippingDao.updateStatusByShippingId(shippingHead);
            //更新订单状态
            orderUpdateService.updateOrderStatus(oderSignHead.getOrderId(), OrderStatus.ORDER_SIGN);
        }
        return true;
    }

//    @Transactional
//    public boolean saveOrUpdateKAData(OrderSignHead oderSignHead, String logSign) {
//        //插入签收单表
//        orderSignHeadMapper.insertSelective(oderSignHead);
//        for (OrderSignDetail detail : oderSignHead.getDetails()) {
//            //签收单detail
//            orderSignDetailMapper.insertSelective(detail);
//        }
//        int sysTime = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
//
//        OrderShippingHead shippingHead = new OrderShippingHead();
//        shippingHead.setStatus(2);
//        shippingHead.setShippingOrderId(oderSignHead.getShippingOrderId());
//        shippingHead.setArrivedAt(sysTime);
//        shippingHead.setActivedAt(sysTime);
//        shippingHead.setReceiptStatus(1);
//        shippingDao.updateStatusByShippingId(shippingHead);
//        //更新订单状态
//        orderUpdateService.updateOrderStatus(oderSignHead.getOrderId(), OrderStatus.ORDER_SIGN);
//
//        return true;
//    }

    @Transactional
    public boolean saveOrUpdateData(OrderSignHead oderSignHead, String logSign, ShippingStatus status) {
        //插入签收单表
        orderSignHeadMapper.insertSelective(oderSignHead);
        for (OrderSignDetail detail : oderSignHead.getDetails()) {
            //签收单detail
            orderSignDetailMapper.insertSelective(detail);
        }
        int sysTime = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());

        OrderShippingHead shippingHead = new OrderShippingHead();
        shippingHead.setStatus(status.getIndex());
        shippingHead.setShippingOrderId(oderSignHead.getShippingOrderId());
        shippingHead.setArrivedAt(sysTime);
        shippingHead.setActivedAt(sysTime);
        shippingHead.setUpdatedAt(sysTime);
        shippingHead.setReceiptStatus(ShippingReceiptStatus.RECEIPT.getIndex());
        shippingDao.updateStatusByShippingId(shippingHead);
        //更新订单状态
        return orderUpdateService.updateOrderStatus(oderSignHead.getOrderId(), OrderStatus.ORDER_SIGN) > 0;
    }

    @Transactional
    public boolean saveOrUpdateSignData(ReceiptHeadDto receiptHeadDto, OrderSignHead oderSignHead, String logSign, OrderStatus orderStatus, ShippingStatus status, CalOrderReturnContent calReturnContent, OrderHead orderHead, OrderSignHead oldHead) {

        //老的单子如果是无效的，则需要设置更新
        if (oldHead != null && oldHead.getIsValid().compareTo(Valid.DISABLE.getValue()) == 0) {
            OrderSignHead updateHead = new OrderSignHead();
            updateHead.setId(oldHead.getId());
            updateHead.setIsValid(Valid.DISABLE.getValue());
            updateHead.setUpdatedAt(DateUtil.getCurrentTime());
            orderSignHeadMapper.updateByPrimaryKeySelective(updateHead);
        }
        //插入签收单表
        orderSignHeadMapper.insertSelective(oderSignHead);
        for (OrderSignDetail detail : oderSignHead.getDetails()) {
            //签收单detail
            orderSignDetailMapper.insertSelective(detail);
        }
        int sysTime = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());

        OrderShippingHead shippingHead = new OrderShippingHead();
        shippingHead.setStatus(status.getIndex());
        shippingHead.setShippingOrderId(oderSignHead.getShippingOrderId());
        shippingHead.setArrivedAt(sysTime);
        shippingHead.setActivedAt(sysTime);
        shippingHead.setUpdatedAt(sysTime);
        shippingHead.setReceiptStatus(ShippingReceiptStatus.RECEIPT.getIndex());
        shippingDao.updateStatusByShippingId(shippingHead);

        return orderUpdateService.updateOrderStatus(oderSignHead.getOrderId(), orderStatus) > 0;
    }

    @Transactional
    public boolean updateArrivalStatus(OrderSignHead oderSignHead, String logSign) {
        return orderSignHeadMapper.updateByReceiptOrderId(oderSignHead) > 0;
    }

//    @Transactional
//    public boolean updatePayStatus(OrderSignHead oderSignHead, Long orderCode, Long shippingOrderId) {
//        int sysTime = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
//        if (shippingOrderId == null || orderCode == null) {
//            return false;
//        }
//        this.updateStatus(orderCode, shippingOrderId);
//
//        return orderSignHeadMapper.updatePayStatusByReceiptOrderId(oderSignHead) > 0;
//    }


    @Transactional(rollbackFor = RuntimeException.class)
    public boolean updateSignPayStatus(OrderSignHead oderSignHead) {

        return orderSignHeadMapper.updatePayStatusByReceiptOrderId(oderSignHead) > 0;
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public boolean updateExtByReceiptOrderId(OrderSignHead oderSignHead) {

        return orderSignHeadMapper.updateExtByReceiptOrderId(oderSignHead) > 0;
    }

    private void updateStatus(Long orderCode, Long shippingOrderId) {
        int sysTime = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
        OrderShippingHead shippingHead = new OrderShippingHead();
        shippingHead.setStatus(2);
        shippingHead.setShippingOrderId(shippingOrderId);
        shippingHead.setArrivedAt(sysTime);
        shippingHead.setActivedAt(sysTime);
        shippingHead.setReceiptStatus(1);
        shippingDao.updateStatusByShippingId(shippingHead);
        //更新订单状态
        orderUpdateService.updateOrderStatus(orderCode, OrderStatus.ORDER_SIGN);
    }


    @Transactional
    public int updateArrivalStatusBatch(List<OrderSignHead> signOrders, String logSign) {
        int resoInt = 0;
        int time = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
        for (OrderSignHead head : signOrders) {
            head.setConfirmAt(time);
            orderSignHeadMapper.updateByReceiptOrderId(head);

            OrderSignHead orderSignHead = getSignHeadBySignCode(head.getReceiptOrderId());
            if (orderSignHead != null) {
                this.updateStatus(orderSignHead.getOrderId(), orderSignHead.getShippingOrderId());
            }

            resoInt += 1;
        }
        return resoInt;
    }

    @Transactional
    public OrderSignHead getSignHead(Long orderCode) {
        return orderSignHeadMapper.selectByOrderCode(orderCode);
    }

    @Transactional
    public int updatSingHeadInfo(Long receiptOrderId, Map<String, Object> updateFields, String logSign) {
        updateFields.put("receiptOrderId", receiptOrderId);
        updateFields.put("updatedAt", DateUtil.getCurrentTime());
        return orderSignHeadMapper.updateHeadByReceiptOrderId(updateFields);
    }

    @Transactional
    public OrderSignHead getSignHeadByOrderId(Long orderCode, boolean detailFlag) {

        OrderSignHead orderSignHead = orderSignHeadMapper.selectByOrderCode(orderCode);
        if (null == orderSignHead) {
            return null;
        }

        if (detailFlag) {
            orderSignHead.setDetails(orderSignDetailMapper.selectByReceiptOrderId(orderSignHead.getReceiptOrderId()));
        }

        return orderSignHead;
    }

    public OrderSignHead getSignHeadBySignCode(Long signOrderCode) {
        return orderSignHeadMapper.selectByReceiptOrderId(signOrderCode);
    }

    public List<OrderSignHead> getBatchSignHeadBySignCode(List<Long> signOrderCodes) {
        Map<String, Object> codes = new HashMap<>(2);
        codes.put("codes", signOrderCodes);
        codes.put("storageTypeId", 1);
        return orderSignHeadMapper.selectBatchByReceiptOrderId(codes);
    }

//    public List<OrderSignHead> getBatchSignHeadByKA(List<Long> signOrderCodes) {
//        Map<String, Object> codes = new HashMap<>(2);
//        codes.put("codes", signOrderCodes);
//        codes.put("zoneId", 5000);
//        return orderSignHeadMapper.selectBatchByReceiptOrderId(codes);
//    }
//
//    public List<OrderSignHead> getBatchSignHeadBy2000KA(List<Long> signOrderCodes) {
//        Map<String, Object> codes = new HashMap<>(2);
//        codes.put("codes", signOrderCodes);
//        codes.put("zoneId", 2000);
//
//        List<OrderSignHead> orderSignHeads = orderSignHeadMapper.selectBatchByReceiptOrderId(codes);
//
//        if (CollectionUtils.isEmpty(orderSignHeads)) {
//            return Collections.EMPTY_LIST;
//        }
//        List<OrderSignHead> orderSignHeadList = new ArrayList<>();
//        for (OrderSignHead orderSignHead : orderSignHeads) {
//            OrderHead filter = new OrderHead();
//            filter.setOrderCode(orderSignHead.getOrderId());
//            OrderHead head = orderHeadDao.findOne(filter);
//
//            String orderDc = JSON.parseObject(head.getExt()).getString("orderDc");
//
//            if (!StringUtils.isEmpty(orderDc) && orderDc.startsWith("DCx")) {
//                orderSignHeadList.add(orderSignHead);
//            }
//        }
//
//        return orderSignHeadList;
//    }


    public OrderSignHead getSignHead(Long receiptOrderId, boolean detailFlag) {
        OrderSignHead orderSignHead = orderSignHeadMapper.selectByReceiptOrderId(receiptOrderId);
        if (detailFlag) {

            orderSignHead.setDetails(orderSignDetailMapper.selectByReceiptOrderId(orderSignHead.getReceiptOrderId()));
        }

        return orderSignHead;
    }


    /**
     * head信息 并拉取detail信息
     *
     * @param signOrderCodes
     * @param detailFlag
     * @return
     */
    public List<OrderSignHead> getBatchSignHeadBySignCode(List<Long> signOrderCodes, boolean detailFlag) {
        Map<String, Object> codes = new HashMap<String, Object>(2);
        codes.put("codes", signOrderCodes);
        codes.put("storageTypeId", 1);//1 常温  2低温
        List<OrderSignHead> orderSignHeads = orderSignHeadMapper.selectBatchByReceiptOrderId(codes);
        if (detailFlag) {
            for (OrderSignHead orderSignHead : orderSignHeads) {
                orderSignHead.setDetails(orderSignDetailMapper.selectByReceiptOrderId(orderSignHead.getReceiptOrderId()));
            }
        }
        return orderSignHeads;
    }

    public List<OrderSignDetail> selectByReceiptOrderId(Long receiptOrderId) {

        return orderSignDetailMapper.selectByReceiptOrderId(receiptOrderId);
    }

    public List<OrderSignHead> selectByTimes(Integer startTime, Integer endTime) {

        return orderSignHeadMapper.selectByTimes(startTime, endTime);
    }

    public List<OrderSignHead> findInvalidReceiptsBetween(Integer startTime, Integer endTime) {
        return orderSignHeadMapper.findInvalidReceiptsBetween(startTime, endTime);
    }

    @Transactional
    public boolean setValid(OrderSignHead oderSignHead, String logSign) {
        return orderSignHeadMapper.setValidByReceiptOrderId(oderSignHead) > 0;
    }

    @Transactional
    public boolean updateReceiptTime(OrderSignHead oderSignHead) {
        return orderSignHeadMapper.updateReceiptTimeByReceiptOrderId(oderSignHead) > 0;
    }

    public Map<String, Object> findOrderSignByReceiptOrderId(Long id) {
        Map<String, Object> head = orderSignHeadMapper.findOrderSignByReceiptOrderId(id);
        if (MapUtils.isEmpty(head)) {
            throw new BusinessException(ReturnCode.REQUEST_FAIL, "CAN NOT FIND ANY RECORD BY ID " + id);
        }
        fillPayChannelIntoHead(head);
        List<Map<String, Object>> items = orderSignDetailMapper.findOrderSignItemsByReceiptOrderId(id);
        Map<String, Object> result = new HashMap<>();
        result.put("head", head);
        result.put("detail", items);
        return result;
    }

    private void fillPayChannelIntoHead(Map<String, Object> head) {
        head.put("payment", getPaymentInfoFromPayService(head));
    }

    private Map<String, Object> getPaymentInfoFromPayService(Map<String, Object> head) {
        Long orderId = (Long) head.get("order_id");
        OrderHead order = orderQueryService.findByCode(orderId, false, false);
        Long venderId = (Long) head.get("vender_id");
        JSONObject responseBody = null;
        if (order.hasPaymentNo()) {
            responseBody = paymentProxy.queryPayInfo(null, order.getPaySequence(), venderId);
        } else {
            Long tradeId = (Long) head.get("receipt_order_id");
            responseBody = paymentProxy.queryPayInfo(String.valueOf(tradeId), null, venderId);
        }
        return (responseBody != null) ? responseBody : Collections.<String, Object>emptyMap();
    }

    private Set<Integer> weighingGoods(Long id) {
        OrderHead order = orderQueryService.findById(id, false, true).orElseThrow(BusinessException::orderNotFound);
        return order.getDetails().stream().filter($ -> $.isWeighingGoods()).map($ -> $.getItemCode()).collect(Collectors.toSet());
    }

    public void createReturnRequest(boolean createReturnImmediately, OrderSignHead receipt, Map<String, Object> party) {
        logger.info("create return request on receipt: {}", receipt.getReceiptOrderId());

        Set<Integer> weighingGoods = weighingGoods(receipt.getOrderId());
        logger.info("weighing goods are: {}", weighingGoods);

        Map<Integer, BigDecimal> signed = new HashMap<>();
        receipt.getDetails().forEach($ -> signed.put($.getItemCode(), weighingGoods.contains($.getItemCode()) ? $.getRealQty() : $.getQty()));
        logger.info("signed items are: {}", signed);

        OrderShippingHead shipping = shippings.findByOrderId(receipt.getOrderId(), true).orElseThrow(BusinessException::shippingNotFound);
        List<Map<?, ?>> unsignedItems = shipping.getDetails().stream().map($ -> {
            BigDecimal shippingQuantity = weighingGoods.contains($.getItemCode()) ? $.getRealQty() : $.getQty();
            BigDecimal signedQuantity = Optional.ofNullable(signed.get($.getItemCode())).orElse(BigDecimal.ZERO);
            BigDecimal unsignedQuantity = subtractWithScale4(shippingQuantity, signedQuantity);
            if (greaterThanZero(unsignedQuantity)) {
                Map<String, Object> item = Maps.of(
                        "item_code", $.getItemCode(),
                        "quantity", unsignedQuantity,
                        "reason", Maps.newHashMap("code", "SHORTCUT", "notes", "", "images", Collections.emptyList())
                );
                if (createReturnImmediately) {
                    item.put("opinion", Maps.of(
                            "approved", true,
                            "discarded", false,
                            "notes", ""
                    ));
                }
                return item;
            } else {
                return Collections.emptyMap();
            }
        }).filter($ -> !$.isEmpty()).collect(Collectors.toList());
        logger.info("unsigned items are: {}", unsignedItems);
        if (!unsignedItems.isEmpty()) {
            returnRequests.create(Maps.newHashMap("occasion", BEFORE_SIGN.name(), "party", party, "order", Maps.newHashMap("id", receipt.getOrderId()), "details", unsignedItems));
        }
    }

    public void createIssueTask(OrderSignHead receipt) {
        OmsTask omsTask = new OmsTask();
        omsTask.setTaskType(TaskType.Bill_SIGN_BY_MQ.getCode());
        omsTask.setRefCode(receipt.getReceiptOrderId().toString());
        omsTask.setVenderId(receipt.getVenderId());
        tasks.add(omsTask);
    }
}
